
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.js"></script>
    <title>日期控件</title>
    <style>
      .box {
        width: 280px;
        position: relative;
        top: 200px;
        left: 550px;
        box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.5);
        padding: 5px;
        border-radius: 4px;
        animation: show 0.5s ease;
      }

    @keyframes show {
        0%{
            opacity: 0;
        }
        100%{
            opacity: 1;
        }
    }

      /* 绘制三角 */
      .box::after,
      .box::before {
        content: "";
        display: block;
        position: absolute;
        top: -10px;
        left: 12px;
        border-left: 10px solid transparent;
        border-right: 10px solid transparent;
        border-bottom: 10px solid #cccccc;
      }
      .box::after {
        top: -9px;
        border-left: 10px solid transparent;
        border-right: 10px solid transparent;
        border-bottom: 10px solid #ffffff;
      }
      /* 标题 */
      .box-header {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
      }
      .box-header__lt,
      .box-header__gt {
        font-family: "宋体";
        width: 30px;
        height: 30px;
        line-height: 30px;
        display: flex;
        justify-content: center;
        border-radius: 2px;
        cursor: pointer;
      }
      .box-header__lt:hover,
      .box-header__gt:hover {
        background-color: #cccccc;
      }
      .box-header__data {
        line-height: 30px;
      }
      /* 主体 */
      
      /* 周一至周日 */
      .center-caption {
        display: grid;
        grid-template-columns: repeat(7, calc(280px / 7));
        grid-template-rows: 36px;
        background-color: #999999;
        border-radius: 4px;
        font-weight: bold;
      }
      /* 单个周日期布局 */
      .center-caption__coll {
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 4px;
      }
      /* 今天 */
      .center-caption__coll__today{
        background-color: rgb(124, 146, 245);
        cursor: pointer;
        color: #ffffff;
      }
      /* 不是当月日期禁用 */
      .center-caption__coll__dark {
        color: #cccccc;
        cursor: no-drop;
      }
      /* 日期鼠标滑动事件 */
      .center-caption__coll:hover {
        background-color: rgb(124, 146, 245);
        cursor: pointer;
        color: #ffffff;
      }
       /* 重写  不是当月日期禁用 */
      .center-caption__coll__dark:hover {
        background-color: transparent;
        color: #cccccc;
        cursor: no-drop;
      }
      /* 日期矩阵 */
      .center-table {
        display: grid;
        grid-template-columns: repeat(7, calc(280px / 7));
        grid-template-rows: repeat(6, 36px);
      }



      .input{
          width: 150px;
          position: absolute;
          top: 30px;
          left: 30px;
      }
    </style>
  </head>
  <body>
    <input class="input" />

  </body>

  <script>
    /*

        1. 点击 input 弹出日期控件面板

        1. 绘制日期矩阵                OK
        2. 区分非本月的日期             OK
        3. 点击日期弹出相应 年月日      OK
        4. 日期要按照月份切换           OK
        5. 点击年和月，能挑选年费和月份
    */

    ;(function(win, doc) {
        // utils
        let utils = {
            formatNum: function(n) {
                n = parseInt(n, 10);
                // 个位数补0
                return n < 10 ? '0' + n : n
            },
            formatDate: function(date) {
                let year = date.getFullYear();
                let month = date.getMonth();
                let d = date.getDate();
                return year + '-' + this.formatNum(month)  + '-' + this.formatNum(d);
            },
            maxDate: function(year, month) {
                return new Date(year, month + 1, 0).getDate();
            },
            firstDate: function(year, month) {
                return new Date(year, month, 1).getDay();
            }
        }

        function dc(tagName, className, innerHtml) {
            let eml = document.createElement(tagName);
            eml.className = className;
            if (innerHtml) {
                eml.innerHTML = innerHtml;
            }
            return eml
        }

        const Calender = function(eml) {
            this.box = dc('div', 'box')
            this.eml = eml;
        }

        // 主渲染逻辑
        Calender.prototype.renderPalen = function(e) {
            if (this.isRender) {
                return
            }
            // 获取 input 的绝对位置
            let position = this.eml.getBoundingClientRect()
            let top = position.top
            let left = position.left
            // 设置日期控件的位置
            this.box.style.top = (top + position.height) + 'px'
            this.box.style.left = left + 'px'
            doc.body.appendChild(this.box)
            
            this.currentData = new Date();

            this.renderHeader();
            this.renderBody();
            this.bindEvent();

            let days = [ '日', '一', '二', '三', '四', '五', '六', ];
            let centerCaption = dc('div', 'center-caption')
            for (var i = 0; i < days.length; i++) {
                centerCaption.appendChild(dc('div', 'center-caption__coll', days[i]))
            }
            // 绘制一周7天
            this.tableContainer.appendChild(centerCaption)
            this.isRender = true
        }

        // 移出节点
        Calender.prototype.removePalen = function() {
            doc.body.removeChild(this.box);
            this.box = dc('div', 'box');
            this.isRender = false;
        }

        // 渲染头部
        Calender.prototype.renderHeader = function() {
            this.header = dc('div', 'box-header ');
            this.prev = dc('div', 'box-header__lt ', '&lt;');
            this.next = dc('div', 'box-header__gt ', '&gt;');
            this.headerDate = dc('div', 'box-header__data ', '2020-08');
            this.table = dc('div', 'center-table');
            this.header.appendChild(this.prev);
            this.header.appendChild(this.headerDate);
            this.header.appendChild(this.next);
            this.box.appendChild(this.header)
        }

        // 设置日期到input
        Calender.prototype.setVal = function(e) {
            let eml = e.target
            if (eml.swatch) {
                this.eml.value = eml.date
            }
        }

        // 渲染日期矩阵
        Calender.prototype.renderBody = function() {
            this.tableContainer = dc('div', 'center-list')
            this.box.appendChild(this.tableContainer)
            this.box.appendChild(this.table)
            
            let year = this.currentData.getFullYear();
            let month = this.currentData.getMonth();
            let date = this.currentData.getDate();
            let firstDate = new Date(year, month, 1);
            firstDate.setDate(1 - utils.firstDate(year, month) + 1)

            this.table.innerHTML = ''
            // 渲染表格
            for (let i = 0; i < 42; i++) {
                let date = new Date(firstDate)
                let div = document.createElement('div');
                div.className = 'center-caption__coll ';
                div.innerHTML = date.getDate();

                div.date = utils.formatDate(date)
                div.swatch = true

                let self = this;
                div.addEventListener('click', function(e) {
                    e.stopPropagation();
                    self.setVal(e)
                })

                if (date.getMonth() !== month) {
                    div.className += 'center-caption__coll__dark '
                    div.swatch = false
                }
                if (
                    date.getMonth() === new Date().getMonth() &&
                    date.getDate() === new Date().getDate()
                ) {
                    div.className += 'center-caption__coll__today '
                }
                this.table.appendChild(div);

                firstDate.setDate(firstDate.getDate() + 1);
            }
        }

        // 设置月份
        Calender.prototype.setMonth = function(n) {
            this.currentData.setMonth(
                this.currentData.getMonth() + n
            )
        }

        // 事件绑定
        Calender.prototype.bindEvent = function() {
            let slef = this
            this.prev.addEventListener('click', function(e) {
                slef.setMonth(-1);
                slef.renderBody();
                e.stopPropagation();
            })
            this.next.addEventListener('click', function(e) {
                slef.setMonth(1);
                slef.renderBody();
                e.stopPropagation();
            })
        }

        // 实现
        window.calender = function(eml) {
            let c = new Calender(eml);

            eml.addEventListener('click', function(e) {
                e.stopPropagation();
            })
            eml.addEventListener('focus', function(e) {
                c.renderPalen()
                e.stopPropagation();
            })

            document.addEventListener('click', function() {
                if (c.isRender === true) {
                    c.removePalen()
                }
            })
        }

    })(window, document);


    // calender('.input')

    $(function($) {
      // 日期控件
      $.fn.extend({
        'calender': function() {
          window.calender(this.get(0))
        }
      });

      $('.input').calender()

    })


  </script>

</html>
